package com.jianbing.dao;

import com.jianbing.exception.ServiceException;
import com.jianbing.mongo.MongoConnections;
import com.jianbing.mongo.MongoHelper;
import com.jianbing.util.StringUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.Block;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;

import org.apache.log4j.Logger;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * \* Created with IntelliJ IDEA. \* User: LFL \* Date: 2017/4/24 \* Time: 17:26
 * \* To change this template use File | Settings | File Templates. \*
 * Description: \
 */

@Repository
public class MongoDBDao {
	private static Logger log = Logger.getLogger(MongoDBDao.class);

	private static String DEFAULT_DB;

	@Value("#{propertiesReader['mongoDB_DEFAULT_DB']}")
	public void setDefaultDB(String mongoDB_DEFAULT_DB) {
		MongoDBDao.DEFAULT_DB = mongoDB_DEFAULT_DB;
	}

	public static void insert(String tableName, Object object) throws ServiceException {
		if (StringUtils.isEmpty(tableName) || object == null) {
			return;
		}
		try {
			Document document = MongoHelper.bean2Document(object);
			insert(tableName, document);
		} catch (Exception e) {
			log.error(e.toString(), e);
			throw new ServiceException("insert fail", e);
		}

	}

	// 重载
	public static void insert(String tableName, Document document) throws ServiceException {
		if (StringUtils.isEmpty(tableName) || document == null) {
			return;
		}
		try {
			MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName).insertOne(document);
		} catch (Exception e) {
			log.error(e.toString(), e);
			throw new ServiceException("insert fail", e);
		}
	}

	public static long update(String tableName, String idName, Object id, Document document) throws ServiceException {
		if (StringUtils.isEmpty(tableName) || id == null || document == null) {
			return 0;
		}
		try {
			MongoCollection<Document> connection = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName);
			Bson object = null;
			if ("_id".equals(idName)) {
				object = Filters.eq("_id", new ObjectId(id.toString()));
			} else if ("id".equals(idName)) {
				object = Filters.eq("id", id);
			} else if ("goodsn".equals(idName)) {
				object = Filters.eq("goodsn", id);
			}
			UpdateResult result = connection.updateOne(object, new Document("$set", document));
			if (result != null) {
				log.info("update success, record num: " + result.getMatchedCount());
				return result.getMatchedCount();
			} else
				return 0;
		} catch (Exception e) {
			log.error(e.toString(), e);
			throw new ServiceException("update fail", e);
		}
	}

	public static long update(String tableName, Object object, Object id) throws ServiceException {
		if (StringUtils.isEmpty(tableName) || id == null || object == null) {
			return 0;
		}
		try {
			MongoCollection<Document> connection = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName);
			Bson bson = Filters.eq("id", id);
			UpdateResult result = connection.updateOne(bson, new Document("$set", MongoHelper.bean2Document(object)));
			if (result != null) {
				log.info("update success, record num: " + result.getMatchedCount());
				return result.getMatchedCount();
			} else
				return 0;
		} catch (Exception e) {
			log.error(e.toString(), e);
			throw new ServiceException("update fail", e);
		}
	}

	public static long update(String tableName, BasicDBObject bson, Object object) throws ServiceException {
		if (StringUtils.isEmpty(tableName) || bson == null || object == null) {
			return 0;
		}
		try {
			MongoCollection<Document> connection = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName);
			UpdateResult result = connection.updateOne(bson, new Document("$set", MongoHelper.bean2Document(object)));
			if (result != null) {
				log.info("update success, record num: " + result.getMatchedCount());
				return result.getMatchedCount();
			} else
				return 0;
		} catch (Exception e) {
			log.error(e.toString(), e);
			throw new ServiceException("update fail", e);
		}
	}

	/**
	 * 更新数据
	 *
	 * @param tableName
	 * @param object
	 * @param keyParameters
	 * @return
	 * @throws ServiceException
	 */

	public static long update(String tableName, Object object, Map<String, Object> keyParameters)
			throws ServiceException {
		try {
			if (keyParameters == null || keyParameters.size() == 0) {
				return 0;
			}

			Bson[] bsonParamArray = new Bson[keyParameters.size()];
			int i = 0;
			for (Map.Entry<String, Object> entry : keyParameters.entrySet()) {
				Bson bson = Filters.eq(entry.getKey(), entry.getValue());
				bsonParamArray[i++] = bson;
			}

			MongoCollection<Document> connection = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName);
			UpdateResult result = connection.updateMany(Filters.and(bsonParamArray),
					new Document("$set", MongoHelper.bean2Document(object)));
			if (result != null) {
				return result.getMatchedCount();
			} else
				return 0;
		} catch (Exception e) {
			log.error(e.toString(), e);
			throw new ServiceException("update fail", e);
		}
	}

	public static List<Document> findAll(String tableName) {
		MongoCollection collection = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName);
		final List<Document> list = new ArrayList<Document>();
		Block<Document> processList = new Block<Document>() {
			@Override
			public void apply(final Document document) {
				try {
					list.add(document);
				} catch (Exception e) {
					log.error(e.toString(), e);
				}
			}
		};
		collection.find().forEach(processList);
		return list;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static <T> List<T> queryForList(String tableName, Class<T> returnType, BasicDBObject queryObject,
			BasicDBObject showObject, BasicDBObject sortObject, Integer skipSize, Integer pageSize) {
		if (StringUtils.isEmpty(tableName)) {
			return null;
		}
		MongoCollection collection = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName);
		final List<T> list = new ArrayList<T>();
		Block<Document> processList = new Block<Document>() {
			@Override
			public void apply(final Document document) {
				try {
					list.add(MongoHelper.document2Bean(document, returnType));
				} catch (Exception e) {
				}
			}
		};
		FindIterable<Document> findIterable = null;
		if (queryObject != null && queryObject.size() > 0) {
			findIterable = collection.find(queryObject);
		} else {
			findIterable = collection.find();
		}
		if (sortObject != null && sortObject.size() > 0) {
			findIterable = findIterable.sort(sortObject);
		}
		if (showObject != null && showObject.size() > 0) {
			findIterable = findIterable.projection(showObject);
		}
		if (skipSize != null && skipSize > 0) {
			findIterable = findIterable.skip(skipSize);
		}
		if (pageSize != null && pageSize > 0) {
			findIterable = findIterable.limit(pageSize);
		}
		findIterable.forEach(processList);
		return list;
	}

	@SuppressWarnings("rawtypes")
	public static <T> T findOne(String tableName, Class<T> returnClass, BasicDBObject queryObject)
			throws ServiceException {
		if (StringUtils.isEmpty(tableName) || returnClass == null) {
			return null;
		}
		MongoCollection collection = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName);
		Document document = (Document) collection.find(queryObject).first();
		return MongoHelper.document2Bean(document, returnClass);
	}

	@SuppressWarnings("rawtypes")
	public static <T> T findOneSort(String tableName, Class<T> returnClass, BasicDBObject queryObject,
			BasicDBObject sortObject) throws ServiceException {
		if (StringUtils.isEmpty(tableName) || returnClass == null) {
			return null;
		}
		MongoCollection collection = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName);
		Document document = (Document) collection.find(queryObject).sort(sortObject).first();
		return MongoHelper.document2Bean(document, returnClass);
	}

	public static <T> List<T> queryForList(String tableName, Map<String, String> params, Class<T> returnType) {
		return queryForList(tableName, params, null, returnType, null, null, null, null);
	}

	public static <T> List<T> queryForList(String tableName, Map<String, String> params,
			Map<String, Integer> sortParameters, final Class<T> returnType, Date startDate, Date endDate,
			Integer skipSize, Integer limitNumber) {
		if (StringUtils.isEmpty(tableName))
			return null;

		MongoCollection collection = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName);

		final List<T> list = new ArrayList<T>();
		int size = 0;

		if (params != null) {
			size = params.size();
		}

		if (startDate != null) {
			size++;
		}
		if (endDate != null) {
			size++;
		}
		Bson[] bsonParamArray = new Bson[size];
		int i = 0;
		Bson bson = null;

		if (params != null && params.size() > 0) {
			for (Map.Entry<String, String> entry : params.entrySet()) {
				bson = Filters.eq(entry.getKey(), entry.getValue());
				bsonParamArray[i++] = bson;
			}
		}

		if (startDate != null) {
			bson = Filters.gte("createDate", startDate);
			bsonParamArray[i++] = bson;
		}

		if (endDate != null) {
			bson = Filters.lte("createDate", endDate);
			bsonParamArray[i++] = bson;
		}

		Block<Document> processList = new Block<Document>() {
			@Override
			public void apply(final Document document) {
				try {
					list.add(MongoHelper.document2Bean(document, returnType));
				} catch (Exception e) {
				}
			}
		};
		Document sortDocument = new Document();
		if (sortParameters != null) {
			for (Map.Entry<String, Integer> sort : sortParameters.entrySet()) {
				sortDocument.append(sort.getKey(), sort.getValue());
			}
		}
		FindIterable<Document> findIterable = null;

		if (bsonParamArray != null && bsonParamArray.length > 0) {
			findIterable = collection.find(Filters.and(bsonParamArray));
		} else {
			findIterable = collection.find();
		}
		findIterable = findIterable.sort(sortDocument);
		if (skipSize != null && skipSize > 0) {
			findIterable = findIterable.skip(skipSize);
		}
		if (limitNumber != null && limitNumber > 0) {
			findIterable = findIterable.limit(limitNumber);
		}
		findIterable.forEach(processList);
		return list;
	}

	/**
	 * 删除数据
	 *
	 * @param tableName
	 * @param params
	 * @return
	 * @throws IllegalAccessException
	 */
	public static long delete(String tableName, Map<String, String> params) throws ServiceException {
		if (params == null || params.size() == 0) {
			return 0;
		}

		Bson[] bsonParamArray = new Bson[params.size()];
		int i = 0;
		for (Map.Entry<String, String> entry : params.entrySet()) {
			Bson bson = Filters.eq(entry.getKey(), entry.getValue());
			bsonParamArray[i++] = bson;
		}

		DeleteResult result = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName)
				.deleteMany(Filters.and(bsonParamArray));
		if (result != null) {
			return result.getDeletedCount();
		}
		return 0;
	}

	public static long deleteByIds(String tableName, List<Object> ids) throws ServiceException {
		if (ids == null || ids.size() == 0) {
			return 0;
		}

		DeleteResult result = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName)
				.deleteMany(Filters.in("id", ids.toArray()));

		if (result != null) {
			return result.getDeletedCount();
		}
		return 0;
	}

	public static long deleteByAppkeys(String tableName, List<Object> appkeys) throws ServiceException {
		if (appkeys == null || appkeys.size() == 0) {
			return 0;
		}

		DeleteResult result = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName)
				.deleteMany(Filters.in("appkey", appkeys.toArray()));

		if (result != null) {
			return result.getDeletedCount();
		}
		return 0;
	}

	public static long delete(String tableName, String idName, Object id) throws ServiceException {
		if (StringUtils.isEmpty(tableName) || id == null) {
			return 0;
		}

		BasicDBObject object = new BasicDBObject(idName, id);
		if ("_id".equals(idName)) {
			object = new BasicDBObject(idName, new ObjectId(id.toString()));
		} else if ("goodsn".equals(idName)) {
			object = new BasicDBObject(idName, new ObjectId(id.toString()));
		}
		DeleteResult result = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName).deleteOne(object);
		if (result != null) {
			return result.getDeletedCount();
		}
		return 0;
	}

	/**
	 * 删除数据
	 *
	 * @param tableName
	 * @return
	 * @throws IllegalAccessException
	 */
	public static long delete(String tableName, BasicDBObject bson) throws ServiceException {
		if (StringUtils.isEmpty(tableName) || bson == null) {
			return 0;
		}

		DeleteResult result = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName).deleteOne(bson);
		if (result != null) {
			return result.getDeletedCount();
		}
		return 0;
	}

	public static long deleteMany(String tableName, BasicDBObject bson) throws ServiceException {
		if (StringUtils.isEmpty(tableName) || bson == null) {
			return 0;
		}
		DeleteResult result = MongoConnections.getDatabase(DEFAULT_DB).getCollection(tableName).deleteMany(bson);
		if (result != null) {
			return result.getDeletedCount();
		}
		return 0;
	}

}
